/*
 * Copyright (c) 2012 Johann Hanne
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

#ifndef _SETJMP_H
#define _SETJMP_H

#include <stdint.h>

typedef uint16_t jmp_buf[14];

int setjmp(jmp_buf env);
#pragma aux setjmp = ""  \
    "    mov word ptr es:[bx+26],ss" /* save SS before we modify it */ \
    \
    "    mov ax, es" \
    "    mov ss, ax" \
    "    mov ax, sp" /* save SP before we modify it */ \
    "    mov sp, bx" \
    \
    "    add sp, 26" /* SS already stored, so only 13 words here */ \
    \
    "    pusha" /* push AX (=o riginal SP), CX, DX, BX, SP (modified/useless), BP, SI, DI */ \
    "    push ds" \
    "    push es" \
    "    pushf" \
    "    push cs" \
    "    call dummy_label_to_push_ip" \
    "dummy_label_to_push_ip:" \
    /* ATTENTION: The following add corrects the instruction pointer to point
       to the END of this assembly stub, so the "12" must be the sum of the
       size of this and the following instructions! If somebody finds out how
       to put the current instruction address into a register directly with
       OpenWatcom asm, this could be optimized out... */ \
    "    add word ptr es:[bx+0], 12" \
    \
    "    mov ss,word ptr es:[bx+26]" /* restore original SS */ \
    "    mov sp, ax" /* restore original SP */ \
    "    xor ax, ax" /* return 0 */ \
    parm [es bx] value [ax] modify [ax];

void longjmp(jmp_buf env, int val);
#pragma aux longjmp = ""  \
    "    lds di,dword ptr es:[bx+24]" /* ds:di now points to stack which was active at setjmp time */ \
    "    mov word ptr ds:[di-6],ax" /* prepare return value */ \
    "    mov ax,word ptr es:[bx+2]" /* prepare original CS */ \
    "    mov word ptr ds:[di-2],ax" \
    "    mov ax,word ptr es:[bx+0]" /* prepare original IP */ \
    "    mov word ptr ds:[di-4],ax" \
    \
    "    mov ax, es" \
    "    mov ss, ax" \
    "    mov sp, bx" \
    "    add sp, 4" /* skip IP/CS */ \
    \
    "    popf" \
    "    pop es" \
    "    pop ds" \
    "    popa" /* we rely on popa not modifying SP */ \
    "    pop ss" \
    "    mov sp, ax" /* setjmp stores the original SP in AX */ \
    \
    "    sub sp, 6" \
    "    pop ax" \
    "    retf" \
    parm [es bx] [ax] modify [ax];

#if 0
void print_jmpbuf(jmp_buf env) {
    printf("IP: 0x%04x\n", env[0]);
    printf("CS: 0x%04x\n", env[1]);
    printf("FLAGS: 0x%04x\n", env[2]);
    printf("ES: 0x%04x\n", env[3]);
    printf("DS: 0x%04x\n", env[4]);
    printf("DI: 0x%04x\n", env[5]);
    printf("SI: 0x%04x\n", env[6]);
    printf("BP: 0x%04x\n", env[7]);
    printf("SP: 0x%04x (modified/useless)\n", env[8]);
    printf("BX: 0x%04x\n", env[9]);
    printf("DX: 0x%04x\n", env[10]);
    printf("CX: 0x%04x\n", env[11]);
    printf("AX: 0x%04x (= original SP)\n", env[12]);
    printf("SS: 0x%04x\n", env[13]);
}
#endif

#endif /* _SETJMP_H */
